package com.zoe.onelink.ratelimit.rule.init;

import com.zoe.onelink.annotation.governance.ratelimit.RateLimit;
import com.zoe.onelink.enhancer.init.AbstractApplicationInitializer;
import com.zoe.onelink.ratelimit.rule.register.RateLimitRuleRegister;
import com.zoe.onelink.ratelimit.util.MethodUtil;
import com.zoe.onelink.util.StrUtil;
import io.opensergo.proto.common.v1.TimeUnit;
import io.opensergo.proto.fault_tolerance.v1.RateLimitStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2022</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2022-12-06
 */
@Slf4j
@RequiredArgsConstructor
public class RateLimitRuleInit extends AbstractApplicationInitializer {

    private final ApplicationContext applicationContext;
    private final RateLimitRuleRegister rateLimitRuleRegister;

    @Override
    protected void doInit(ApplicationStartedEvent event) throws Exception {
        this.load();
    }

    public void load() {
        log.info("[OneLinkFrm] 开始加载限流规则");
        List<RateLimitStrategy> rules = this.applicationContext.getBean(RequestMappingHandlerMapping.class)
                .getHandlerMethods().entrySet()
                .stream()
                .filter(e -> !e.getKey().getPatternsCondition().getPatterns().isEmpty())
                .filter(e -> e.getValue().hasMethodAnnotation(RateLimit.class))
                .map(e -> {
                    HandlerMethod handlerMethod = e.getValue();
                    RateLimit rateLimit = handlerMethod.getMethodAnnotation(RateLimit.class);
                    String resourceId = StrUtil.isBlank(rateLimit.value())
                            ? MethodUtil.getMethodSign(handlerMethod.getMethod())
                            : rateLimit.value();
                    return createRule(rateLimit, resourceId);
                }).collect(Collectors.toList());

        log.info("[OneLinkFrm] 共找到{}条规则，开始注册规则...", rules.size());
        this.rateLimitRuleRegister.registerRules(rules);
        log.info("[OneLinkFrm] 限流规则注册完成");
    }

    private static RateLimitStrategy createRule(RateLimit rateLimit, String resourceId) {
        RateLimitStrategy.MetricType metricType = RateLimitStrategy.MetricType.valueOf(rateLimit.metricType().name());
        return RateLimitStrategy.newBuilder()
                .setName(resourceId)
                .setMetricType(metricType)
                .setThreshold(rateLimit.thresholdPerSeconds())
                .setStatDuration(1)
                .setStatDurationTimeUnit(TimeUnit.SECOND)
                .setLimitMode(RateLimitStrategy.LimitMode.MODE_LOCAL)
                .build();
    }

    @Override
    public boolean isAsync() {
        return false;
    }

    @Override
    public boolean throwable() {
        return false;
    }

}
